﻿namespace DomainServices.Tools
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.ServiceModel.DomainServices;
    using System.Xml.XPath;

    internal static class SilverlightAppDomainUtilities
    {
        private const string FrameworkManifestKey = "FrameworkManifest";

        internal static void ConfigureAppDomain(string silverlightFrameworkDirectory)
        {
            FrameworkManifest frameworkManifest = GetFrameworkManifest(silverlightFrameworkDirectory);
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(SilverlightAppDomainUtilities.ResolveFrameworkAssemblyVersioning);
            AppDomain.CurrentDomain.SetData("FrameworkManifest", frameworkManifest);
        }

        private static FrameworkManifest GetFrameworkManifest(string silverlightFrameworkDirectory)
        {
            FrameworkManifest manifest = new FrameworkManifest();
            List<FrameworkManifestEntry> list = new List<FrameworkManifestEntry>();
            XPathDocument document = new XPathDocument(silverlightFrameworkDirectory + "slr.dll.managed_manifest");
            XPathNodeIterator iterator = document.CreateNavigator().Select("/manifest/*[name and publickeytoken and version]");
            while (iterator.MoveNext())
            {
                iterator.Current.MoveToChild("name", string.Empty);
                string str = iterator.Current.Value;
                iterator.Current.MoveToParent();
                iterator.Current.MoveToChild("publickeytoken", string.Empty);
                string publickeytoken = iterator.Current.Value;
                iterator.Current.MoveToParent();
                iterator.Current.MoveToChild("version", string.Empty);
                string version = iterator.Current.Value;
                iterator.Current.MoveToParent();
                byte[] buffer = (from x in Enumerable.Range(0, publickeytoken.Length)
                    where 0 == (x % 2)
                    select Convert.ToByte(publickeytoken.Substring(x, 2), 0x10)).ToArray<byte>();
                FrameworkManifestEntry item = new FrameworkManifestEntry {
                    Name = str,
                    Version = new Version(version),
                    PublicKeyTokenBytes = buffer
                };
                list.Add(item);
            }
            manifest.Assemblies = list.ToArray();
            return manifest;
        }

        private static Assembly ResolveFrameworkAssemblyVersioning(object sender, ResolveEventArgs args)
        {
            System.Func<FrameworkManifestEntry, bool> func2 = null;
            FrameworkManifest data = (FrameworkManifest) AppDomain.CurrentDomain.GetData("FrameworkManifest");
            AssemblyName requestedAssembly = new AssemblyName(args.Name);
            if ((requestedAssembly.Version.CompareTo(data.SystemVersion) < 0) && requestedAssembly.IsSystemAssembly())
            {
                //var func =  null;
                //if (func2 == null)
                //{
                //    func2 = assembly => assembly.Name == requestedAssembly.Name;
                //}
                //FrameworkManifestEntry silverlightAssembly = Enumerable.Where<FrameworkManifestEntry>(data.Assemblies, func2).SingleOrDefault<FrameworkManifestEntry>();
                //if (silverlightAssembly != null)
                //{
                //    if (func == null)
                //    {
                //        func = h__TransparentIdentifier5 => ((h__TransparentIdentifier5.assemblyName.Name == silverlightAssembly.Name) && h__TransparentIdentifier5.assemblyName.GetPublicKeyToken().SequenceEqual<byte>(silverlightAssembly.PublicKeyTokenBytes)) && (h__TransparentIdentifier5.assemblyName.Version.CompareTo(silverlightAssembly.Version) == 0);
                //    }
                //    return (from h__TransparentIdentifier5 in Enumerable.Where(from assembly in AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies() select new { assembly = assembly, assemblyName = assembly.GetName() }, func) select h__TransparentIdentifier5.assembly).SingleOrDefault<Assembly>();
                //}
            }
            return null;
        }

        internal class FrameworkManifest : MarshalByRefObject
        {
            private SilverlightAppDomainUtilities.FrameworkManifestEntry[] _assemblies;

            internal SilverlightAppDomainUtilities.FrameworkManifestEntry[] Assemblies
            {
                get
                {
                    return this._assemblies;
                }
                set
                {
                    this._assemblies = value;
                    this.SystemVersion = (from assembly in this._assemblies
                        where assembly.Name == "System"
                        select assembly.Version).SingleOrDefault<Version>();
                }
            }

            internal Version SystemVersion { get; private set; }
        }

        internal class FrameworkManifestEntry : MarshalByRefObject
        {
            internal string Name { get; set; }

            internal byte[] PublicKeyTokenBytes { get; set; }

            internal System.Version Version { get; set; }
        }
    }
}

